package cn.edu.neu.lab603.cloudeval.playground

import java.util.Random

import cn.edu.neu.lab603.cloudeval.CloudEval
import cn.edu.neu.lab603.cloudeval.entity.model.power.EmpiricalPowerModel
import cn.edu.neu.lab603.cloudeval.entity.model.{ConstantResModel, RandomResModel}
import cn.edu.neu.lab603.cloudeval.entity.{PhysicalMachine => Host, VirtualMachine => VM, _}
import cn.edu.neu.lab603.cloudeval.monitor._
import cn.edu.neu.lab603.cloudeval.strategy.policy._
import cn.edu.neu.lab603.cloudeval.strategy.{SimpleOverloadAllocationStrategy, VMMigrationStrategy}
import cn.edu.neu.lab603.des.{MetricsLogMonitor, SimMetric}

import scala.language.implicitConversions

/** 大规模随机数据中心测试方案。
  *
  * Created by yfwz100 on 2016/11/3.
  */
object LargeStochasticDCRunner extends App {

  val simulation = new CloudEval

  val execDelays = new CloudletExecDelayMetric
  simulation.addMonitor(execDelays)

  val waitingTimeMetric = new CloudletWaitingTimeMetric
  simulation.addMonitor(waitingTimeMetric)

  val migrationTimes = new MigrationTimesMetric
  simulation.addMonitor(migrationTimes)

  val powerMetric = new PowerMetric(new EmpiricalPowerModel)
  simulation.addMonitor(powerMetric)

  val cloudletSLAMetric = new CloudletSLAMetric
  simulation.addMonitor(cloudletSLAMetric)

  val hostBootTimesMetric = new CounterMetric(classOf[Host.BootEvent])
  simulation.addMonitor(hostBootTimesMetric)

  val hostShutdownTimesMetric = new CounterMetric(classOf[Host.ShutdownEvent])
  simulation.addMonitor(hostShutdownTimesMetric)

  val vmBootTimesMetric = new CounterMetric(classOf[VM.BootEvent])
  simulation.addMonitor(vmBootTimesMetric)

  val vmShutdownTimesMetric = new CounterMetric(classOf[VM.ShutdownEvent])
  simulation.addMonitor(vmShutdownTimesMetric)

  val migrationDurationMonitor = new MigrationDownTimeMonitor
  simulation.addMonitor(migrationDurationMonitor)

  val resSaturateMonitor = new ResSaturateMetric(Map(
    "core" -> (0.0, 1.0),
    "mips" -> (0.2, 0.9),
    "ram" -> (0.0, 1.0),
    "bw" -> (0.0, 1.0)
  ))
  simulation.addMonitor(resSaturateMonitor)

  val networkUtilityMonitor = new NetworkUtilityMonitor
  simulation.addMonitor(networkUtilityMonitor)

  val liveTimeMonitor = new CloudletLiveTimeMonitor
  simulation.addMonitor(liveTimeMonitor)

  val utilizationMonitor = new HostUtilizationMonitor
  simulation.addMonitor(utilizationMonitor)

  //  val hostSelectionPolicy = new HostSelectionPolicyStaticThreshold(1.0)
  val hostSelectionPolicy = new HostSelectionPolicyLR(
    utilizationMonitor,
    new HostSelectionPolicyStaticThreshold(1.0)
  )
  val migrationStrategy = new VMMigrationStrategy(
    hostSelectionPolicy.getReallocationTimePolicy(60),
    hostSelectionPolicy,
    //    new VMSelectionPolicyMaximumCorrelation
    new VMSelectionPolicyMinimumMigrationTime
    //        new VMSelectionPolicyRandomSelection
    //    new VMSelectionPolicyMinimumUtilization
  )
  //  val migrationStrategy = new EarlyStoppingReallocationStrategy

  val dc = simulation.createDataCenter(
    new SimpleOverloadAllocationStrategy(2),
    migrationStrategy,
    (0 until 10).map(_ => new Host(Map(
      "core" -> 1,
      "mips" -> 1000,
      "ram" -> 4096,
      "bw" -> 10240
    ))) ++ (0 until 10).map(_ => new Host(Map(
      "core" -> 2,
      "mips" -> 2000,
      "ram" -> 8192,
      "bw" -> 10240
    ))) ++ (0 until 150).map(_ => new Host(Map(
      "core" -> 4,
      "mips" -> 4000,
      "ram" -> 32768,
      "bw" -> 10240
    )))
  )

  val cloudletStart = 10
  val cloudletLen = 60 * 60
  val random = new Random
  val user = simulation.createUser(
    (0 until 50).map { _ =>
      new UserCloudTask(// 类型 1
      cloudletStart, // 开始时间
      cloudletLen, // 长度
      Map(
        "core" -> ConstantResModel(1),
        "mips" -> RandomResModel(600, 900, 1000),
        "ram" -> RandomResModel(256, 512, 512),
        "bw" -> ConstantResModel(0, 512)
      ),
      dc
    )
    } ++ (0 until 100).map { _ =>
      new UserCloudTask(// 类型 2
      cloudletStart, // 开始时间
      cloudletLen, // 长度
      Map(
        "core" -> ConstantResModel(1),
        "mips" -> RandomResModel(300, 800, 1000),
        "ram" -> RandomResModel(51, 102, 102),
        "bw" -> ConstantResModel(0, 512)
      ),
      dc
    )
    } ++ (0 until 50).map { _ =>
      new UserCloudTask(// 类型 3
      cloudletStart, // 开始时间
      cloudletLen, // 长度
      Map(
        "core" -> ConstantResModel(1),
        "mips" -> RandomResModel(500, 950, 1000),
        "ram" -> RandomResModel(512, 2048, 2048),
        "bw" -> ConstantResModel(0, 512)
      ),
      dc
    )
    } ++ (0 until 10).map { _ =>
      new UserCloudTask(// 类型 4
      cloudletStart, // 开始时间
      cloudletLen, // 长度
      Map(
        "core" -> ConstantResModel(2),
        "mips" -> RandomResModel(1000, 1900, 2000),
        "ram" -> RandomResModel(512, 1024, 1024),
        "bw" -> ConstantResModel(0, 512)
      ),
      dc
    )
    } ++ (0 until 50).map { _ =>
      new UserCloudTask(// 类型 5
      cloudletStart, // 开始时间
      cloudletLen, // 长度
      Map(
        "core" -> ConstantResModel(2),
        "mips" -> RandomResModel(1000, 1500, 2000),
        "ram" -> RandomResModel(1024, 2048, 2048),
        "bw" -> ConstantResModel(0, 512)
      ),
      dc
    )
    } ++ (0 until 10).map { _ =>
      new UserCloudTask(// 类型 6
      cloudletStart, // 开始时间
      cloudletLen, // 长度
      Map(
        "core" -> ConstantResModel(3),
        "mips" -> RandomResModel(1500, 2000, 2500),
        "ram" -> RandomResModel(2048, 4096, 4096),
        "bw" -> ConstantResModel(0, 512)
      ),
      dc
    )
    }
  )

  val logMonitor = new MetricsLogMonitor(
    s"/Users/yfwz100/tmp/$migrationStrategy-u270-h170.csv",
    Seq(
      // simple statistics
      "hostBootTimes" -> hostBootTimesMetric,
      "hostShutTimes" -> hostShutdownTimesMetric,
      "vmBootTimes" -> vmBootTimesMetric,
      "vmShutTimes" -> vmShutdownTimesMetric,
      // power
      "power" -> powerMetric,
      // performance
      "execDelays" -> execDelays,
      "waitingTimes" -> waitingTimeMetric,
      "overallSlav" -> cloudletSLAMetric.overallSLAMetric,
      "averageSlav" -> cloudletSLAMetric.averageSLAMetric,
      "vmMigrations" -> migrationTimes,
      "vmMigrationDuration" -> SimMetric(migrationDurationMonitor)(_.stats.values.map(_.sum).sum),
      "averageLiveTime" -> liveTimeMonitor.getAverageFrustrationMetric(),
      // balance
      "resSaturate" -> resSaturateMonitor.getLastMetric,
      "vmBalance" -> SimMetric(networkUtilityMonitor)(_.getBalanceLevel(simulation))
    )
  )
  simulation.addMonitor(logMonitor)

  simulation.run(5 * cloudletLen)

  println()

  val finishedCloudlets = user.getCloudlets.count(_.getStatus == CloudTask.Status.STOPPED)
  val readyCloudlets = user.getCloudlets.count(_.getStatus == CloudTask.Status.READY)
  val runningCloudlets = user.getCloudlets.count(_.getStatus == CloudTask.Status.RUNNING)

  println(s"Experiment Name: $migrationStrategy")
  println(s"Finished cloudlets: $finishedCloudlets")
  println(s"Ready cloudlets: $readyCloudlets")
  println(s"Running cloudlets: $runningCloudlets")

  implicit def toInt[T](metric: SimMetric[T]): T = metric.getCurrentValue

  println(s"Number of hosts boot: $hostBootTimesMetric")
  println(s"Number of hosts shutdown: $hostShutdownTimesMetric")
  println(s"Number of VM boot: $vmBootTimesMetric")
  println(s"Number of VM shutdown: $vmShutdownTimesMetric")

  println(s"Number of migrations: $migrationTimes")
  println(s"Duration of migrations: ${logMonitor.metrics("vmMigrationDuration")}")
  println(s"Percent of frustrations: ${logMonitor.metrics("averageLiveTime")}")
  println(s"Saturation of data center: $resSaturateMonitor")

  print(s"Cloudlet execution delays: $execDelays ")
  if (execDelays.stats.nonEmpty)
    println(f"(Average: ${execDelays / execDelays.stats.size.toDouble}%2.3f)")
  else
    println()

  print(s"Waiting Time: $waitingTimeMetric")
  if (waitingTimeMetric.stats.nonEmpty)
    println(f"(Average: ${waitingTimeMetric / waitingTimeMetric.stats.size}%2.3f)")
  else
    println()

  println(f"Overall percent of SLA violations: ${cloudletSLAMetric.overallSLAMetric * 100}%2.3f%%")
  println(f"Average percent of SLA violations: ${cloudletSLAMetric.averageSLAMetric * 100}%2.3f%%")
  println(f"Power Consumption: ${powerMetric / 3600000.0}%4.3fkWh")
}
